一场雨

motan 概述及启动

概述

官方文档:https://github.com/weibocom/motan/wiki/zh_userguide

register 模块

用来和注册中心进行交互,包括注册服务、订阅服务、服务变更通知、服务心跳发送等功能;Server 端会在系统初始化时通过 register 模块注册服务,Client 端在系统初始化时会通过 register 模块订阅到具体提供服务的 Server 列表,当 Server 列表发生变更时也由 register 模块通知 Client。

protocol 模块

用来进行 RPC 服务的描述和 RPC 服务的配置管理,这一层还可以添加不同功能的 filter用来完成统计、并发限制等功能。

serialize 模块

将 RPC 请求中的参数、结果等对象进行序列化与反序列化,即进行对象与字节流的互相转换;默认使用对 Java 更友好的 hessian2 进行序列化。

transport 模块

用来进行远程通信,默认使用 Netty NIO 的 TCP 长链接方式。

cluster 模块

Client 端使用的模块,cluster 是一组可用的 Server 在逻辑上的封装,包含若干可以提供 RPC 服务的 Server,实际请求时会根据不同的高可用与负载均衡策略选择一个可用的 Server 发起远程调用。

在进行 RPC 请求时,Client 通过代理机制调用 cluster 模块,cluster 根据配置的 HA 和 LoadBalance 选出一个可用的 Server,通过serialize模块把RPC请求转换为字节流,然后通过 transport 模块发送到 Server 端。

配置元素

Motan 框架中将功能模块抽象为四个可配置的元素,分别为:

protocol:服务通信协议。服务提供方与消费方进行远程调用的协议,默认为 Motan 协议,使用 hessian2 进行序列化,Netty 作为 Endpoint 以及使用Motan 自定义的协议编码方式。

registry:注册中心。服务提供方将服务信息(包含IP、端口、服务策略等信息)注册到注册中心,服务消费方通过注册中心发现服务。当服务发生变更,注册中心负责通知各个消费方。

service:服务提供方提供的服务。使用方将核心业务抽取出来,作为独立的服务。通过暴露服务并将服务注册至注册中心,从而使调用方调用。

referer:服务消费方对服务的引用,即服务调用方。

Motan 推荐使用 Spring 配置 RPC 服务,目前 Motan 扩展了 6 个自定义 Spring XML 标签

motan:protocol
motan:registry
motan:basicService
motan:service
motan:basicReferer
motan:referer

高可用方面是 Motan 的一大特点,支持多种服务治理和高可用机制,包括:灵活多样的集群负载均衡策略,支持 ActiveWeight/Random/RoundRobin/LocalFirst/Consistent等6种策略,并支持自定义扩展;

自动集成Failover、Failfast容错策略,实现故障节点自动摘除,自动探测恢复,有效进行服务故障隔离,远离服务卡死及雪崩;

连接池自定义控制,根据业务场景灵活配置;支持多机房间调用流量压缩、动态流量调整,实现真正的跨 IDC 的高可用。基于高并发、高负载场景的优化,具备在高压力场景下的高可用能力。

郑大侠的笔记 motan

http://code.zhizus.com/categories/Motan%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB/

Motan源码解读-SPI机制

SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。

java的spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

Motan源码解读-Client异步消息

接下来我们总结一下motan中callbackMap的逻辑:

client发送request前,先将request注册到(register)到callbackMap中,如果发送前连接异常,则直接remove掉。客户端收到response后,根据requestId找到对应的request上下文,并移除callbackMap中的对应的对象。对于服务端超时或者其他异常,客户端收不到消息的情况,则通过TimeoutMonitor来定时扫描callbackMap来清理。

Motan源码解读-负载均衡

RPCClient首先会经过一层HA(这里motan实现了两种容灾策略,failfast&failover),然后会根据配置的loadbalance的策略选取相应的nettyClient发送请求到RPCServer。

Alt text

Alt text

// 可以基于SPI扩展,使用方可以实现自己的LoadBalance
    @Spi(scope = Scope.PROTOTYPE)
    public interface LoadBalance<T> {

        //当可用的应用列表变化时会调用这个方法刷新(motan的可用引用列表是基于服务发现这种模式实现,目前实现了consul&zk)
        void onRefresh(List<Referer<T>> referers);

        // 基于负载均衡的策略选择可用的引用
        Referer<T> select(Request request);

        // FailoverHaStrategy会使用到这个,多线程场景
        void selectToHolder(Request request, List<Referer<T>> refersHolder);

        // ?? 仅仅属于WeightLoadBalance的特性,定义在最上层接口是否合适??
        //cluster.getLoadBalance().setWeightString(weights); 这里使用的时候判断一下类型,对WeightLoadBalance这种类型单独处理向下转型是否可以?
        // 或者是否有其他更好的办法??
        void setWeightString(String weightString);

    }

Motan源码解读-容错策略

Motan源码解读-Protocol

provider:
        服务提供方,通常可以认为是server端

export:
        服务提供Provider暴露出去就变成Exporter,服务使用方(客户端)可以调用暴露出去的Provider。

        根据URL将exporter从map中取出来,然后,将provider放到exporter中去,然后,再将 exporter放到map中去。

refer:
        引用服务,引用的服务会通过配置去调用服务端暴露的服务

Motan源码解读-源码结构

motan-benchmark // 压测相关
motan-core    //motan核心代码,也是源码解析的主要包
motan-demo    // 示例,快速上手的最佳方法
motan-extension // 扩展
motan-manager // 管理后台
motan-registry-consul    //基于consul的服务发现实现
motan-registry-zookeeper // 基于zookeeper的服务发现实现
motan-springsupport // spring容器的支持,也支持springboot
motan-transport-netty // netty传输的封装

Motan源码解读-Filter

很多服务都有filter这个概念,filter就像在管道前的过滤器,只要通过管道的请求必须先经过过滤器。过滤器可以做很多事情,类似权限控制,限流,统计,上报,监控等等。有些为服务架构会把filter单独抽出来作为getway服务

Motan源码解读-codec

motan的codec负责rpc框架通讯协议的编解码。

manager 模块的启动

1.在顶层install 然后,在 manager项目中打包 war包 ,使用 package 命令 。

2.用tomcat启动项目。

motan 的启动

Alt text

Alt text

Alt text

motan_demo_server.xml中: xmlns:motan=”http://api.weibo.com/schema/motan

spring.handlers中:

http\://api.weibo.com/schema/motan=com.weibo.api.motan.config.springsupport.MotanNamespaceHandler

1.获取命名空间uri :

com.weibo.api.motan.config.springsupport.MotanNamespaceHandler

public class MotanNamespaceHandler extends NamespaceHandlerSupport {

    public final static Set<String> protocolDefineNames = new ConcurrentHashSet<String>();
    public final static Set<String> registryDefineNames = new ConcurrentHashSet<String>();
    public final static Set<String> basicServiceConfigDefineNames = new ConcurrentHashSet<String>();
    public final static Set<String> basicRefererConfigDefineNames = new ConcurrentHashSet<String>();

    @Override
    public void init() {

        registerBeanDefinitionParser("referer", new MotanBeanDefinitionParser(RefererConfigBean.class, false));
        registerBeanDefinitionParser("service", new MotanBeanDefinitionParser(ServiceConfigBean.class, true));
        registerBeanDefinitionParser("protocol", new MotanBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("registry", new MotanBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("basicService", new MotanBeanDefinitionParser(BasicServiceInterfaceConfig.class, true));
        registerBeanDefinitionParser("basicReferer", new MotanBeanDefinitionParser(BasicRefererInterfaceConfig.class, true));
        registerBeanDefinitionParser("spi", new MotanBeanDefinitionParser(SpiConfigBean.class, true));
        registerBeanDefinitionParser("annotation", new MotanBeanDefinitionParser(AnnotationBean.class, true));
        Initializable initialization = InitializationFactory.getInitialization();

        initialization.init();

    }
}

2.通过反射机制实例化,调用init();方法。

方法中注册了Motan自定DOM节点的解析器

MotanNamespaceHandler继承了NamespaceHandlerSupport。所以不用实现所有的解析工作,只需将自定义schema中的元素解析器注册进来就可以。(schema拓展的解析器)

MotanBeanDefinitionParser(Class<?> beanClass, boolean required);//元素解析器

public class MotanBeanDefinitionParser implements BeanDefinitionParser{


}

3.将实例存入handlerMappings用于后续的解析

4.找到对应DOM节点的解析器,开始解析,

根据MotanNamespaceHandler中注册的信息,spring找到解析类并调用解析方法:MotanBeanDefinitionParser.Parse()。

第一步,初始化RootBeanDefinition

第二步,获取beanid

第三步,将xml中配置的信息放到beandefinition的PropertyValues中。

bd.getPropertyValues().addPropertyValue("id", id);

最后将BeanDefinition返回出来。配置成bean的类会定义成BeanDefinition,注册到spring。

5.registerBeanPostProcessors();

控制台输出结果

MotanNamespaceHandler 中的init()方法 start
AllSpiInitialization init.
AllSpiInitialization init finish.
MotanNamespaceHandler 中的init()方法 end
ServiceConfigBean 中 afterPropertiesSet() 方法 start
ServiceConfigBean 中 checkAndConfigBasicConfig 方法 basicServiceConfigDefineNames 集合中的size :1
ServiceConfigBean 中 checkAndConfigBasicConfig 方法 basicServiceConfigDefineNames 集合中的name :serviceBasicConfig
ServiceConfigBean 中 checkAndConfigBasicConfig 方法
ServiceConfigBean 中 checkAndConfigExport 方法 String p : demoMotan
ServiceConfigBean 中 afterPropertiesSet() 方法 end
ServiceConfigBean 中 afterPropertiesSet() 方法 start
ServiceConfigBean 中 checkAndConfigBasicConfig 方法 basicServiceConfigDefineNames 集合中的size :1
ServiceConfigBean 中 checkAndConfigBasicConfig 方法 basicServiceConfigDefineNames 集合中的name :serviceBasicConfig
ServiceConfigBean 中 checkAndConfigBasicConfig 方法
ServiceConfigBean 中 checkAndConfigExport 方法 String p : demoMotan
ServiceConfigBean 中 afterPropertiesSet() 方法 end
ServiceConfigBean 中onApplicationEvent方法:export(); start
AbstractInterfaceConfig 中checkInterfaceAndMethods()方法
AbstractInterfaceConfig 中 loadRegistryUrls() registries的size():1*****以及name:registry
ServiceConfig 中的 export():doExport(protocolConfig, port, registryUrls);  strat
SimpleConfigHandler 中 export方法: Exporter<T> exporter = protocol.export(provider, serviceUrl); start
exporter = createExporter(provider, url); 开始
NettyEndpointFactory create share_channel server: url={}motan://10.1.195.135:8001/com.weibo.motan.demo.service.MotanDemoService?group=motan-demo-rpc
AbstractServer 的构造方法
createServer end  
exporter = createExporter(provider, url); 结束
exporter.init(); 开始
AbstractNode init()
 DefaultRpcProtocol中 DefaultRpcExporter  doInit()
public synchronized boolean open()中NettyServer ServerChannel start Open: url=motan://10.1.195.135:8001/?group=motan-demo-rpc
result:true
exporter.init(); 结束
SimpleConfigHandler 中 export方法: Exporter<T> exporter = protocol.export(provider, serviceUrl); end
doExport结束
ServiceConfig 中的 export():doExport(protocolConfig, port, registryUrls);  end
ServiceConfigBean 中onApplicationEvent方法:export(); end
...(另一个service)
server start...